﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace RoadSystemTest
{
    /// <summary>
    /// Interaction logic for RoadSystem.xaml
    /// </summary>
    public partial class RoadSystem : UserControl
    {               
        private const string MSG_EMPTY = "ÜRES";
        private const string MSG_PAUSED = "MEGÁLLíTVA";
        private bool _isInEditMode;
        public bool IsInEditMode
        {
            get { return _isInEditMode; }            
        }
        private bool _isPaused;
        public bool IsPaused
        {
            get { return _isPaused; }
        }
        private bool _isStarted;
        public bool IsStarted
        {
            get { return _isStarted; }
        }

        private List<Road> Roads { get; set; }
        private List<Road> GenerateRoads { get; set; }
        private List<CrossRoad> CrossRoads { get; set; }
        private List<Car> Cars { get; set; }

        public int Lights { get; private set; }

        public void SetLightInterval(int i)
        {
            Lights = i;
            foreach (CrossRoad c in CrossRoads)
            {
                c.SetInterval(Lights);
            }
        }

        private ITrafficObject _selectedObject;
        public ITrafficObject SelectedObject
        {
            get
            {
                return _selectedObject;
            }
            private set
            {
                _selectedObject = value;
                SelectObjectChanged();
            }
        }

        //Az autók mozgásáért felelős timer
        System.Windows.Threading.DispatcherTimer timer;

        //Az autók generálásáért felelős timer;
        System.Windows.Threading.DispatcherTimer timerGen;

        private bool _lanePointsVisible;
        public bool LanePointsVisible
        {
            get
            {
                return _lanePointsVisible;
            }
            set
            {
                _lanePointsVisible = value;
                RefreshLanePoints();
            }
        }

        public RoadSystem()
        {
            InitializeComponent();
            timer = new DispatcherTimer();
            timer.Tick += timer_Tick;
            timer.Interval = new TimeSpan(0, 0, 0, 0, 10);
            timerGen = new DispatcherTimer();
            timerGen.Tick += timerGen_Tick;
            timerGen.Interval = new TimeSpan(0, 0, 0, 2, 0);
            Roads = new List<Road>();
            GenerateRoads = new List<Road>();
            CrossRoads = new List<CrossRoad>();
            Cars = new List<Car>();

            SelectedObject = null;
            SetLightInterval(2);
        }

        void RefreshLanePoints()
        {
            foreach (ITrafficObject r in Roads)
            {
                r.SetLanePointsVisible(_lanePointsVisible);
            }
            foreach (ITrafficObject cr in CrossRoads)
            {
                cr.SetLanePointsVisible(_lanePointsVisible);
            }
        }

        void timer_Tick(object sender, EventArgs e)
        {
            for (int i=Cars.Count - 1; i>=0; --i)
            {
                Cars[i].SetCaption(Cars[i].Position.ToString());
                Cars[i].MoveLane();         
            }
        }

        void timerGen_Tick(object sender, EventArgs e)
        {
            //GenerateCarToRoad(Roads[0].ForwardLane);
            //GenerateCarToRoad(Roads[1].BackwardLane);
            //GenerateCarToRoad(Roads[2].BackwardLane);
            //GenerateCarToRoad(Roads[3].ForwardLane);
            GenerateCar();
        }

        public void SetCarGenerator(Road r, bool isGenerate)
        {
            if (isGenerate)
            {
                if (GenerateRoads.IndexOf(r) < 0)
                {
                    GenerateRoads.Add(r);
                }
            }
            else
            {
                GenerateRoads.Remove(r);
            }
        }

        public bool GetCarGenerator(Road r)
        {
            return GenerateRoads.IndexOf(r) >= 0;
        }

        public void GenerateCar()
        {
            foreach (Road r in GenerateRoads)
            {
                Car car = new Car(new Random().Next(1, 3));
                Panel.SetZIndex(car, 50);
                cnvMain.Children.Add(car);
                car.OnDeadEnd += car_OnDeadEnd;                
                if (!r.AddCar(car))
                {
                    //car.Visibility = Visibility.Hidden;
                    cnvMain.Children.Remove(car);
                    car = null;
                    GC.Collect();
                    GC.WaitForPendingFinalizers();
                }
                else
                {
                    Cars.Add(car);
                    Caption.Content = "Cars.Count = " + Cars.Count.ToString();
                } 
            }
        }

        private void GenerateCarToRoad(Lane lane)
        {
            Car car = new Car(new Random().Next(1,3));
            Panel.SetZIndex(car, 50);
            cnvMain.Children.Add(car);
            car.OnDeadEnd += car_OnDeadEnd;
            if (!lane.AddCar(car))
            {
                //car.Visibility = Visibility.Hidden;
                cnvMain.Children.Remove(car);
                car = null;
                GC.Collect();
                GC.WaitForPendingFinalizers();
            }
            else
            {
                Cars.Add(car);
                Caption.Content = "Cars.Count = " + Cars.Count.ToString();
            }            
        }

        private void UserControl_Loaded_1(object sender, RoutedEventArgs e)
        {
            //Betöltés után
            HideMsg();

            //Pár komponens létrehozása tesztként
            /*Road road;
            road = new Road(Sites.Horizontal, new Point { X = 10, Y = 310 });
            road.OnLaneClick += road_OnLaneClick;
            road.MouseLeftButtonDown += to_MouseLeftButtonDown;
            cnvMain.Children.Add(road);
            Roads.Add(road);
           
            Road road2 = new Road(Sites.Horizontal, new Point {X = 410, Y = 310});
            road2.OnLaneClick += road_OnLaneClick;
            road2.MouseLeftButtonDown += to_MouseLeftButtonDown;
            cnvMain.Children.Add(road2);
            Roads.Add(road2);

            Road road3 = new Road(Sites.Vertical, new Point { X = 210, Y = 110 });
            road3.OnLaneClick += road_OnLaneClick;
            road3.MouseLeftButtonDown += to_MouseLeftButtonDown;
            cnvMain.Children.Add(road3);
            Roads.Add(road3);

            Road road4 = new Road(Sites.Vertical, new Point { X = 210, Y = 510 });
            road4.OnLaneClick += road_OnLaneClick;
            road4.MouseLeftButtonDown += to_MouseLeftButtonDown;
            cnvMain.Children.Add(road4);
            Roads.Add(road4);

            CrossRoad croad;
            croad = new CrossRoad(new Point { X = 310, Y = 310 });
            croad.OnLaneClick += road_OnLaneClick;
            croad.MouseLeftButtonDown += to_MouseLeftButtonDown;
            cnvMain.Children.Add(croad);
            Panel.SetZIndex(croad, 30);
            CrossRoads.Add(croad);

            road.ForwardLane.OnExitLane += lane_ExitLane;
            road.BackwardLane.OnExitLane += lane_ExitLane;
            road2.ForwardLane.OnExitLane += lane_ExitLane;
            road2.BackwardLane.OnExitLane += lane_ExitLane;
            road3.ForwardLane.OnExitLane += lane_ExitLane;
            road3.BackwardLane.OnExitLane += lane_ExitLane;
            road4.ForwardLane.OnExitLane += lane_ExitLane;
            road4.BackwardLane.OnExitLane += lane_ExitLane;

            croad.ConnectEastRoad(road2);
            croad.ConnectWestRoad(road);
            croad.ConnectNorthRoad(road3);
            croad.ConnectSouthRoad(road4);*/
        }

        public void AddRoad(Road r)
        {
            r.OnLaneClick += road_OnLaneClick;
            r.MouseLeftButtonDown += to_MouseLeftButtonDown;
            r.OnCarExitLane += road_CarExitLane;
            cnvMain.Children.Add(r);
            Roads.Add(r);
        }

        public void RemoveRoad(Road r)
        {
            r.OnLaneClick -= road_OnLaneClick;
            r.MouseLeftButtonDown -= to_MouseLeftButtonDown;
            r.OnCarExitLane -= road_CarExitLane;
            cnvMain.Children.Remove(r);
            Roads.Remove(r);
        }

        public void AddCrossRoad(CrossRoad c)
        {
            c.OnLaneClick += road_OnLaneClick;
            c.MouseLeftButtonDown += to_MouseLeftButtonDown;
            cnvMain.Children.Add(c);
            CrossRoads.Add(c);
        }

        public void TranlateAllObjects(double x, double y)
        {
            foreach (Road r in Roads)
            {
                double oldX = r.GetPosition().X;
                double oldY = r.GetPosition().Y;
                r.SetPosition(new Point(oldX + x, oldY + y));
            }
            foreach (CrossRoad cr in CrossRoads)
            {
                double oldX = cr.GetPosition().X;
                double oldY = cr.GetPosition().Y;
                cr.SetPosition(new Point(oldX + x, oldY + y));
            }
        }
        

        private void to_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (_isInEditMode)
            {
                if (SelectedObject != null)
                {
                    SelectedObject.Deselect();
                }
                if ((sender as ITrafficObject) != SelectedObject)
                {
                    (sender as ITrafficObject).Select();
                    SelectedObject = (sender as ITrafficObject);
                }
                else
                {
                    SelectedObject = null;
                }
            }
        }

        private void road_OnLaneClick(object sender, TrafficEventArgs e)
        {
            UIElement ui = sender as UIElement;
            Point startPos = e.lane.GetStartPosition();
            Point endPos = e.lane.GetEndPosition();
            Point visibleStartPos = e.laneStart;
            Point visibleEndPos = e.laneEnd;
            Point cVisibleStartPos = new Point(ui.TranslatePoint(new System.Windows.Point(visibleStartPos.X, visibleStartPos.Y), this));
            Point cVisibleEndPos = new Point(ui.TranslatePoint(new System.Windows.Point(visibleEndPos.X, visibleEndPos.Y), this));
            MessageBox.Show("start=" + startPos.ToString() + ",end=" + endPos.ToString() + "\n" +
                "visibleStart=" + visibleStartPos.ToString() + "visibleEnd=" + visibleEndPos.ToString() + "\n" +
                "CvisibleStart=" + cVisibleStartPos.ToString() + "CvisibleEnd=" + cVisibleEndPos.ToString());
        }

        void road_CarExitLane(object sender, TrafficEventArgs e)
        {
            // e.lane : melyik sávból került ki az e.car
            // e.car : az autó, ami kikerült e.lane ból
            ILaneObject lane = e.lane;
            ICarObject car = e.car;
            cnvMain.Children.Remove((UIElement)e.car);
            Cars.Remove((Car)e.car);
            GC.Collect();
            GC.WaitForPendingFinalizers();
            Caption.Content = "Cars.Count = " + Cars.Count.ToString();
        }

        void car_OnDeadEnd(object sender, TrafficEventArgs e)
        {
            Lane lane = (Lane)e.lane;
            Car car = (Car)e.car;
        }

        public void ShowMsg(string msg, int opacity = 100)
        {
            lblMsg.Visibility = Visibility.Visible;
            lblMsg.Content = msg;
            lblMsg.Opacity = opacity;
        }
        public void ShowMsgEmpty()
        {
            ShowMsg(MSG_EMPTY, 100);
        }
        public void ShowMsgPaused()
        {
            ShowMsg(MSG_PAUSED, 50);
        }
        public void HideMsg()
        {
            lblMsg.Visibility = Visibility.Hidden;
            lblMsg.Content = "";
        }

        private void StopLights(bool isStart = false)
        {
            foreach (CrossRoad cr in CrossRoads)
            {
                if (isStart)
                {
                    cr.StartLights();
                }
                else
                {
                    cr.StopLights();
                }
            }
        }

        private void ResetTrafficObjects()
        {
            // Minden objektum alaphelyzetbe állítása (autok törlése, lámpák)

            // Autókat kivesszük a forgalomból
            foreach (ITrafficObject r in Roads)
            {
                r.Reset();
            }
            foreach (ITrafficObject cr in CrossRoads)
            {
                cr.Reset();
            }

            // A világból is kivesszük őket
            foreach (Car c in Cars)
            {
                cnvMain.Children.Remove(c);
            }
            Cars.Clear();

            GC.Collect();
            GC.WaitForPendingFinalizers();
        }

        private void RefreshBackground()
        {
            if (!_isInEditMode)
            {
                BackgroundColor1.Color = (Color)ColorConverter.ConvertFromString("#FF519E40");
                BackgroundColor2.Color = (Color)ColorConverter.ConvertFromString("#FF37632D");
            }
            else
            {
                BackgroundColor1.Color = (Color)ColorConverter.ConvertFromString("#FF9FB999");
                BackgroundColor2.Color = (Color)ColorConverter.ConvertFromString("#FF343A32");
            }
        }

        public void PauseTraffic()
        {
            timer.Stop();
            timerGen.Stop();
            StopLights();
            if (_isPaused || !_isStarted || _isInEditMode)
            {
                return;
            }
            _isPaused = true;
            //ShowMsgPaused();
        }
        public void ContinueTraffic()
        {
            timer.Start();
            timerGen.Start();
            StopLights(true);
            if (!_isPaused || !_isStarted || _isInEditMode)
            {
                return;
            }
            _isPaused = false;
            //HideMsg();
        }
        public void StartTraffic()
        {
            timer.Start();
            timerGen.Start();
            StopLights(true);
            if (_isStarted)
            {
                return;
            }
            _isStarted = true;
            //HideMsg();
        }
        public void EndTraffic()
        {
            //Forgalom megállítása és minden autó törlése
            timer.Stop();
            timerGen.Stop();
            StopLights();
            ResetTrafficObjects();
            if (!_isStarted)
            {
                return;
            }
            _isStarted = false;
            //HideMsg();
        }

        public void StartEdit()
        {
            //Szerkesztés elkezdése
            if (_isStarted || _isInEditMode)
            {
                return;
            }
            _isInEditMode = true;
            RefreshBackground();
        }
        public void EndEdit()
        {
            // Szerkesztés vége
            if (_isStarted || !_isInEditMode)
            {
                return;
            }
            _isInEditMode = false;
            if (SelectedObject != null)
            {
                SelectedObject.Deselect();
            }
            RefreshBackground();
        }

        public TrafficEventHandler OnSelectObjectChanged;
        private void SelectObjectChanged()
        {
            if (OnSelectObjectChanged != null)
                OnSelectObjectChanged(this, new TrafficEventArgs { trafficObject = SelectedObject });
        }
    }
}
